﻿using System;
using System.Collections.Generic;
using Actors.Actions;
using Common;
using UnityEngine;
using Action = Actors.Actions.Action;

namespace Actors
{
    public class CharacterInitParams : ActorInitParams
    {
        
    }
    
    public struct BodyInfo
    {
        public Bounds bounds;
        public Vector3 center;
        public float height;
        public float radius;
        public Vector3 posOffset;
        public BodyInfo( Vector3 center, float radius, float height )
        {
            this.center = center;
            this.radius = radius;
            this.height = height;
            var min = new Vector3( center.x - radius, center.y - height * 0.5f, center.z - radius );
            var max = new Vector3( center.x + radius, center.y + height * 0.5f, center.z + radius );
            bounds = new Bounds();
            bounds.SetMinMax( min, max );
            posOffset = Vector3.zero;
        }
    }
    
    public abstract class Character : Actor
    {
        private readonly Dictionary<Type, ActionExecutor<Action>> m_ActionExecutors = new Dictionary<Type, ActionExecutor<Action>>();

        #region Members
        
        protected CharacterVisual m_CharacterVisual;

        public BodyInfo BodyInfo { get; private set; }
        
        private Vector3 m_Velocity;
        public Vector3 Velocity => m_Velocity;
        public float Speed { get; protected set; } = 20f;

        #endregion

        #region 重写方法

        protected override void InitCustom()
        {
            EnableUpdate = true;
            EnableFixedUpdate = true;
            BodyInfo = new BodyInfo(new Vector3(0f, 0.9f, 0f), 0.23f, 1.8f);
        }

        protected override void InitVisual()
        {
            m_CharacterVisual = new CharacterVisual(this);
            m_ActorVisual = m_CharacterVisual;
            
            var prefab = ResourceLoader.Load<GameObject>("Model/MainCharacter");
            UnityEngine.Object.Instantiate(prefab, Vector3.zero, Quaternion.identity, gameObject.transform);
        }

        public override void Update()
        {
            base.Update();
            m_CharacterVisual.Update();
        }

        #endregion


        #region Execute Action
        
        public override bool CanExecute(Action action)
        {
            return m_ActionExecutors.ContainsKey(action.GetType());
        }

        public override bool ExecuteAction(Action action)
        {
            var type = action.GetType();
            if (!m_ActionExecutors.TryGetValue(type, out ActionExecutor<Action> executor) )
            {
                return false;
            }

            if (executor == null)
            {
                return false;
            }
            return executor(action);
        }
        
        protected void BindActionExecutor<T>( ActionExecutor<T> executor) where T : Action
        {
            m_ActionExecutors.Add(typeof(T), (cmd) => executor(cmd as T));
        }
        
        protected void UnbindActionExecutor<T>() where T : Action
        {
            m_ActionExecutors.Remove(typeof(T));
        }
        
        protected void UnbindAllActionExecutors()
        {
            m_ActionExecutors.Clear();
        }
        
        protected void ChangeActionExecutor<T>(ActionExecutor<T> executor) where T : Action
        {
            if (!m_ActionExecutors.ContainsKey(typeof(T)))
            {
                return;
            }
            m_ActionExecutors[typeof(T)] = (cmd) => executor(cmd as T);
        }



        #endregion


        #region Movement

        public virtual void Move(Vector2 movementInput)
        {
            if (ReferenceEquals(transform, null))
            {
                return;
            }

            m_Velocity.x = movementInput.x;
            m_Velocity.y = 0;
            m_Velocity.z = movementInput.y;
            transform.Translate(m_Velocity);
        }
        
        public virtual void Move(Vector3 movementInput)
        {
            if (ReferenceEquals(transform, null))
            {
                return;
            }

            m_Velocity = movementInput;
            transform.Translate(m_Velocity);
        }

        #endregion

        #region Animation

        public void PlayAnimation(string stateName)
        {
            m_CharacterVisual.PlayAnimation(stateName);
        }
        

        #endregion


        #region 人物数据获取

        public Vector3 GetCapsuleBottomHemisphere()
        {
            return transform.position + (transform.up * BodyInfo.radius);
        }
        
        public Vector3 GetCapsuleCenter()
        {
            return transform.position + transform.up * BodyInfo.center.y;
        }
        
        public Vector3 GetCapsuleTopHemisphere()
        {
            return transform.position + (transform.up * (BodyInfo.height - BodyInfo.radius));
        }

        #endregion
    }
}